package Patterns;

import java.util.*;
import java.io.*;



public class PatternGenerator 
{
	//This class generates random patterns
	private Random random;
	private PrintWriter writer;

	public Pattern generateRandomPattern(ArrayList<Integer> occurrenceVector,
			boolean randomMode, int vdBound) 
	{
		//This function transforms the given occurrence vector into a random pattern 
		int length = 0;
		int varNumber = occurrenceVector.size();
		Pattern randomPattern = new Pattern();
		int randomVarIndex = -1;
		ArrayList<Integer> randomOrder = new ArrayList<Integer>();
		PatternAnalyser patternAnalyser = new PatternAnalyser();

		//Determine the length of the pattern
		int[] occArray = new int[occurrenceVector.size()];
		for (int i = 0; i < occurrenceVector.size(); i++) {
			occArray[i] = occurrenceVector.get(i);
		}

		for (int i = 0; i < varNumber; i++) {
			length = length + occurrenceVector.get(i);
		}
		
		ArrayList<Integer> possibleVariables = new ArrayList<Integer>();
		for (int j = 0; j < varNumber; j++) 
		{
			possibleVariables.add(j);
		}

		int NumberOfVariablesSet = 0;
		for (int i = 0; i < length; i++) 
		{
			//set a variable at position i
			randomVarIndex = (random.nextInt(varNumber));
			while ((!possibleVariables.contains(randomVarIndex))
					|| (occurrenceVector.get(randomVarIndex) == 0)) 
			{
				randomVarIndex = (random.nextInt(varNumber));
			}
			int temp = occurrenceVector.get(randomVarIndex);
			temp = temp - 1;
			occurrenceVector.set(randomVarIndex, temp);
			if (!randomOrder.contains(randomVarIndex)) 
			{
				NumberOfVariablesSet++;
			}
			randomOrder.add(randomVarIndex);
			// Compute the set of variables that are allowed to occur next
			int index = randomOrder.size() - 1;
			possibleVariables.clear();
			while ((possibleVariables.size() < vdBound + 1) && (index >= 0)) 
			{
				if (!possibleVariables.contains(randomOrder.get(index))) 
				{
					possibleVariables.add(randomOrder.get(index));
				}
				index--;
			}
				if (possibleVariables.size() < vdBound + 1) 
			{
				possibleVariables.clear();
				for (int j = 0; j < varNumber; j++) 
				{
					possibleVariables.add(j);
				}
			} 
			else if (occurrenceVector.get(possibleVariables
					.get(possibleVariables.size() - 1)) == 0) 
			{
				possibleVariables.clear();
				for (int j = 0; j < varNumber; j++) 
				{
					possibleVariables.add(j);
				}
			}
		}
		
		randomPattern.setVarNumber(varNumber);
		randomPattern.setLength(length);
		randomPattern.setVarOrder(randomOrder);
		randomPattern.setOccurrenceVector(occArray);

		patternAnalyser.canonicalRename(randomPattern);

		int varDist = patternAnalyser.computeVariableDistance(randomPattern);
		randomPattern.setVarDistance(varDist);

		return randomPattern;
	}

	public void printRandomPattern(int patternNumber,
			ArrayList<int[]> minMaxOccurrenceVector, boolean randomMode,
			int vdBound, int alphabetSize, int wordLength) 
	{
		//This function prints random patterns into a file
		int avgWordLength = 0;
		int VarDistSum = 0;
		int counter = 0;
		
		//Per each pattern, generate a positive and a negative instance
		while (counter < patternNumber) 
		{
			//create random occurrence vector
			ArrayList<Integer> occurrenceVector = computeOccurrenceVector(minMaxOccurrenceVector);
			//obtain random pattern from the occurrence vector
			Pattern randPattern = generateRandomPattern(occurrenceVector,
					randomMode, vdBound);

			VarDistSum = VarDistSum + randPattern.getVarDistance();
			//CrossDegSum = CrossDegSum + randPattern.getCrossingDegree();

			//Generate two input words: a member and a non member
			String randomMember = GenerateRandomMember(randPattern,
					wordLength, alphabetSize, 1);
			String randomNonMember = GenerateRandomMember(randPattern,
					randomMember.length(), alphabetSize, 0);

			avgWordLength = avgWordLength + randomMember.length()
					+ randomNonMember.length();

			//Write both generated instances to the file
			writer.println(randPattern.toString());
			writer.println(randomMember);
			writer.println(randPattern.toString());
			writer.println(randomNonMember);

			counter++;

			writer.flush();
		}

		//Compute average input word length
		avgWordLength = avgWordLength / (patternNumber * 2);

		writer.println("-");
		writer.println("");
		writer.println("");
		writer.println("-");

		writer.println("Number of patterns: " + patternNumber);
		writer.println("Number of variables: " + minMaxOccurrenceVector.size());
		writer.println("Alphabet size: " + alphabetSize);
		//writer.println("Wordlength: " + wordLength);
		if (vdBound != -1) {
			writer.println("Variable Distance: " + vdBound);
		}
		writer.println("min. occurrences: " + minMaxOccurrenceVector.get(0)[0]);
		writer.println("max. occurrences: " + minMaxOccurrenceVector.get(0)[1]);
		writer.println("Average wordlength: " + avgWordLength);

		writer.flush();
	}

	private ArrayList<Integer> computeOccurrenceVector(
			ArrayList<int[]> minMaxOccurrenceVector) 
	{
		ArrayList<Integer> occurrenceVector = new ArrayList<Integer>();

		//for each variable in the pattern, choose a number of occurrences 
		//uniformly between the min. and max. occurrence number 
		for (int i = 0; i < minMaxOccurrenceVector.size(); i++) 
		{
			occurrenceVector.add(minMaxOccurrenceVector.get(i)[0]
					+ (random.nextInt(minMaxOccurrenceVector.get(i)[1]
							- minMaxOccurrenceVector.get(i)[0] + 1)));
		}

		return occurrenceVector;
	}

	public PatternGenerator(String inputFilename) 
	{
		random = new Random();
		//Initialise PrintWriter
		try {
			writer = new PrintWriter(inputFilename);
		} catch (Exception e) {
			System.err.println(e.getMessage());
		}
	}

	public String GenerateRandomMember(Pattern pattern, int wordLength,
			int alphabetSize, int type) {
		// type 0: Random word with length wordLength

		// type 1: Random member. To determine the lengths of the substitution
		// words, for each variable a random
		// length between zero and the largest number possible depending on the
		// number of occurrences of this
		// variable and of wordLength is determined. The order of the variables
		// is also randomised.

		// type 2: Random member. All possible (with respect to wordLength)
		// substitution word lengths are
		// computed and then one is chosen randomly.
		// DO NOT USE! EXTENSIVE RUNTIME!

		String word = "";
		String[] substWords;
		int[] substWordLengths = new int[pattern.getVarNumber()];
		ArrayList<Integer> randomVarOrder = new ArrayList<Integer>();

		switch (type) 
		{
		case 0:
			//Create random word uniformly symbol by symbol
			for (int i = 0; i < wordLength; i++) 
			{
				word = word + random.nextInt(alphabetSize);
			}
			return word;
		case 1:
			substWords = new String[pattern.getVarNumber()];
			while (randomVarOrder.size() < pattern.getVarNumber()) 
			{
				int randVarIndex = random.nextInt(pattern.getVarNumber());
				if (!randomVarOrder.contains(randVarIndex)) 
				{
					randomVarOrder.add(randVarIndex);
				}
			}

			while (!randomVarOrder.isEmpty()) {
				int currentVar = randomVarOrder.get(0);
				randomVarOrder.remove(0);
				if (wordLength < 0) {
					wordLength = 0;
				}
				int substWordLengthsUpperBound = (wordLength / (pattern
						.getOccurrenceVector()[currentVar] * 3));
				substWordLengths[currentVar] = random
						.nextInt(substWordLengthsUpperBound + 3);
				wordLength = wordLength
						- (substWordLengths[currentVar] * pattern
								.getOccurrenceVector()[currentVar]);
			}

			for (int i = 0; i < substWords.length; i++) {
				String currentSubstWord = "";
				for (int j = 0; j < substWordLengths[i]; j++) {
					currentSubstWord = currentSubstWord
							+ random.nextInt(alphabetSize);
				}
				substWords[i] = currentSubstWord;
			}

			for (int i = 0; i < pattern.getLength(); i++) {
				word = word + substWords[pattern.getVarOrder().get(i)];
			}
			return word;
//		OBSOLETE
//		case 2:
//			ArrayList<Integer[]> allPossibleSubstWordLengths = computeAllPossibleSubstWordLengths(
//					wordLength, pattern.getOccurrenceVector());
//			Integer[] substitutionWordLengths = allPossibleSubstWordLengths
//					.get(random.nextInt(allPossibleSubstWordLengths.size()));
//			substWords = new String[pattern.getVarNumber()];
//
//			for (int i = 0; i < substWords.length; i++) {
//				String currentSubstWord = "";
//				for (int j = 0; j < substitutionWordLengths[i]; j++) {
//					currentSubstWord = currentSubstWord
//							+ random.nextInt(alphabetSize);
//				}
//				substWords[i] = currentSubstWord;
//			}
//
//			for (int i = 0; i < pattern.getLength(); i++) {
//				word = word + substWords[pattern.getVarOrder().get(i)];
//			}
//			return word;
		}
		
		return word;
	}

//OBSOLETE
//	private ArrayList<Integer[]> computeAllPossibleSubstWordLengths(
//			int wordLength, int[] occurrenceVector) {
//		ArrayList<Integer[]> allPossibleSubstWordLengths = new ArrayList<Integer[]>();
//		if (wordLength == 0) {
//			// Every counter bound has to be 0
//			Integer[] substWordLengthsVector = new Integer[occurrenceVector.length];
//			for (int i = 0; i < substWordLengthsVector.length; i++) {
//				substWordLengthsVector[i] = 0;
//			}
//			allPossibleSubstWordLengths.add(substWordLengthsVector);
//			return allPossibleSubstWordLengths;
//		}
//
//		if (occurrenceVector.length == 1) {
//			Integer[] substWordLengthsVector = new Integer[1];
//			substWordLengthsVector[0] = (int) Math.floor(wordLength
//					/ occurrenceVector[0]);
//			allPossibleSubstWordLengths.add(substWordLengthsVector);
//			return allPossibleSubstWordLengths;
//		}
//
//		// For every possible length for the first element
//		for (int i = 0; i <= Math.floor(wordLength / occurrenceVector[0]); i++) {
//			// create shorter Occurrence Vector which will be passed to the next
//			// recursive call
//			int newWordLength = wordLength - (i * occurrenceVector[0]);
//			int[] newOccurrenceVector = new int[occurrenceVector.length - 1];
//			for (int j = 0; j < newOccurrenceVector.length; j++) {
//				newOccurrenceVector[j] = occurrenceVector[j + 1];
//			}
//
//			// Get the list of the recursively computed Counter Bounds Vectors
//			ArrayList<Integer[]> otherSubstWordLengths = computeAllPossibleSubstWordLengths(
//					newWordLength, newOccurrenceVector);
//			// For all of these other otherCounterBounds, we add the first
//			// Counter Bound (i.e. i)
//			for (int j = 0; j < otherSubstWordLengths.size(); j++) {
//				Integer[] substWordLengthsVector = new Integer[occurrenceVector.length];
//				substWordLengthsVector[0] = i;
//				for (int k = 0; k < otherSubstWordLengths.get(j).length; k++) {
//					substWordLengthsVector[k + 1] = otherSubstWordLengths
//							.get(j)[k];
//				}
//				allPossibleSubstWordLengths.add(substWordLengthsVector);
//			}
//		}
//
//		return allPossibleSubstWordLengths;
//	}

}
